1 module hip.windowing.platforms.windows;
2 import hip.windowing.input;
3 import hip.windowing.events;
4 
5 
6 version(UWP){}
7 else version(Windows)
8     version = WindowsNative;
9 
10 version(WindowsNative)
11 {
12     import core.sys.windows.winuser;
13     import core.sys.windows.wingdi;
14     import core.sys.windows.winbase : GetModuleHandle,
15                                     GetLastError,
16                                     FormatMessage,
17                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
18                                     FORMAT_MESSAGE_FROM_SYSTEM,
19                                     LocalFree;
20     import core.sys.windows.windef;
21 
22     alias HWND = void*;
23     alias HINSTANCE = void*;
24     package const(wchar)* winClassName = "HipremeEngine";
25     package __gshared HDC hdc;
26     package HGLRC glContext;
27 
28     pragma(lib, "opengl32");
29     pragma(lib, "gdi32");
30     pragma(lib, "user32"); //Can't import that to UWP
31     pragma(lib, "kernel32");//Can't import that to UWP
32     nothrow ushort LOWORD(ulong l) {return cast(ushort) l;}
33     nothrow ushort HIWORD(ulong l) {return cast(ushort) (l >>> 16);}
34     nothrow @system int GET_X_LPARAM(LPARAM lp){return cast(int)cast(short)LOWORD(lp);}
35     nothrow @system int GET_Y_LPARAM(LPARAM lp){return cast(int)cast(short)HIWORD(lp);}
36     nothrow @system uint GET_XBUTTON_WPARAM(WPARAM wp){ return cast(uint)HIWORD(wp);}
37 
38 
39     package extern(Windows) LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) nothrow @system
40     {
41         switch(msg)
42         {
43             case WM_CLOSE:
44                 if(onWindowClosed != null)
45                     onWindowClosed();
46                 DestroyWindow(hwnd);
47                 break;
48             case WM_DESTROY:
49                 PostQuitMessage(0);
50                 ReleaseDC(hwnd, hdc);
51                 break;
52             case WM_CHAR:
53             case WM_SYSCHAR:
54                 if(onTextInput != null)
55                     onTextInput(cast(wchar)wParam);
56                 break;
57             case WM_SYSKEYDOWN:
58             case WM_KEYDOWN:
59                 if(onKeyDown != null)
60                     onKeyDown(cast(uint)wParam);
61                 break;
62             case WM_SYSKEYUP:
63             case WM_KEYUP:
64                 if(onKeyUp != null)
65                     onKeyUp(cast(uint)wParam);
66                 break;
67             case WM_SIZE: //Resize
68             {
69                 UINT width = LOWORD(lParam);
70                 UINT height = HIWORD(lParam);
71                 if(onWindowResize != null)
72                     onWindowResize(width, height);
73                 break;
74             }
75             case WM_MOVE:
76             {
77                 break;
78             }
79             case WM_MOUSEMOVE:
80             {
81                 if(onMouseMove != null)
82                     onMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
83                 break;
84             }
85             case WM_LBUTTONDOWN:
86                 if(onMouseDown != null)
87                     onMouseDown(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
88                 break;
89             case WM_MBUTTONDOWN:
90                 if(onMouseDown != null)
91                     onMouseDown(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
92                 break;
93             case WM_RBUTTONDOWN:
94                 if(onMouseDown != null)
95                     onMouseDown(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
96                 break;
97             case WM_XBUTTONDOWN:
98                 if(onMouseDown != null)
99                     onMouseDown(
100                         GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2,
101                         GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
102                     );
103                 break;
104             case WM_LBUTTONUP:
105                 if(onMouseUp != null)
106                     onMouseUp(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
107                 break;
108             case WM_MBUTTONUP:
109                 if(onMouseUp != null)
110                     onMouseUp(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
111                 break;
112             case WM_RBUTTONUP:
113                 if(onMouseUp != null)
114                     onMouseUp(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
115                 break;
116             case WM_XBUTTONUP:
117                 if(onMouseUp != null)
118                     onMouseUp(
119                         GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2,
120                         GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
121                     );
122                 break;
123             case WM_MOUSEWHEEL:
124                 if(onMouseWheel != null)
125                     onMouseWheel(
126                         0, cast(int)GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA
127                     );
128                 break;
129             default:
130                 return DefWindowProc(hwnd, msg, wParam, lParam);
131         }
132         return 0;
133     }
134 
135     extern(Windows) nothrow @nogc HGLRC wglCreateContextAttribs(HDC, DWORD, HWND);
136     alias wglChoosePixelFormatARBProc = extern(Windows) nothrow @nogc BOOL function(
137         HDC hdc, const(int)* piAttribFList, const float* pfAttribIList, uint nMaxFormats,
138         int* piFormats, uint* nNumFormats);
139 
140     alias wglCreateContextAttribsARBProc = extern(Windows) nothrow @nogc HGLRC function(
141         HDC hdc, HGLRC hShareContext,const int* attribList
142     );
143 
144     alias wglSwapIntervalEXTProc =  extern(Windows) nothrow @nogc int function(int interval);
145 
146 
147     wglSwapIntervalEXTProc wglSwapIntervalEXT;
148     wglChoosePixelFormatARBProc wglChoosePixelFormatARB;
149     wglCreateContextAttribsARBProc wglCreateContextAttribsARB;
150     extern(Windows) nothrow @nogc void* wglGetProcAddress(const(char)* funcName);
151 
152 
153     extern(Windows) nothrow @nogc bool initializeOpenGL(ref HWND hwnd, int majorVersion, int minorVersion)
154     {
155         PIXELFORMATDESCRIPTOR pfd =
156         {
157             PIXELFORMATDESCRIPTOR.sizeof,
158             1,
159             PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER ,    // Flags
160             PFD_TYPE_RGBA,        // The kind of framebuffer. RGBA or palette.
161             32,                   // Colordepth of the framebuffer.
162             0, 0, 0, 0, 0, 0,
163             0,
164             0,
165             0,
166             0, 0, 0, 0,
167             24,                   // Number of bits for the depthbuffer
168             8,                    // Number of bits for the stencilbuffer
169             0,                    // Number of Aux buffers in the framebuffer.
170             PFD_MAIN_PLANE,
171             0,
172             0, 0, 0
173         };
174         int formatIndex = ChoosePixelFormat(hdc, &pfd);
175         if(formatIndex == 0)
176         {
177             MessageBox(NULL, "Could not choose pixel format!", "Error!", MB_ICONERROR | MB_OK);
178             return false;
179         }
180         if(!SetPixelFormat(hdc, formatIndex, &pfd))
181         {
182             MessageBox(NULL, "Could not set pixel format!", "Error!", MB_ICONERROR | MB_OK);
183             return false;
184         }
185         glContext = wglCreateContext(hdc);
186         if(glContext is null)
187         {
188             MessageBox(NULL, "Could not create OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
189             return false;
190         }
191         if(!wglMakeCurrent(hdc, glContext))
192         {
193             MessageBox(NULL, "Coult not set OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
194             return false;
195         }
196         if(majorVersion < 3 && minorVersion < 3) //This is not actually tested
197         {
198             if(!GetPixelFormat(hdc))
199             {
200                 MessageBox(NULL, "Could not get window pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK);
201                 return false;
202             }
203             if(!DescribePixelFormat(hdc, formatIndex, pfd.sizeof, &pfd))
204             {
205                 MessageBox(NULL, "Could not get describe pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK);
206                 return false;
207             }
208             if((pfd.dwFlags & PFD_SUPPORT_OPENGL) != PFD_SUPPORT_OPENGL)
209             {
210                 MessageBox(NULL, "PixelFormatDescriptor does not support opengl!", "Error!", MB_ICONEXCLAMATION | MB_OK);
211                 return false;
212             }
213             return true;
214         }
215         else
216             return initializeModernOpenGL(hwnd, majorVersion, minorVersion);
217     }
218 
219     package bool initializeModernOpenGL(ref HWND hwnd, int majorVersion, int minorVersion) nothrow @nogc
220     {
221         //Load Function Pointers
222         wglChoosePixelFormatARB = cast(wglChoosePixelFormatARBProc)wglGetProcAddress("wglChoosePixelFormatARB");
223         if(wglChoosePixelFormatARB is null)
224         {
225             MessageBox(NULL, "Could not load wglChoosePixelFormatARB", "Error", MB_ICONERROR | MB_OK);
226             return false;
227         }
228         wglCreateContextAttribsARB = cast(wglCreateContextAttribsARBProc)wglGetProcAddress("wglCreateContextAttribsARB");
229         if(wglCreateContextAttribsARB is null)
230         {
231             MessageBox(NULL, "Could not load wglCreateContextAttribsARB", "Error", MB_ICONERROR | MB_OK);
232             return false;
233         }
234         wglSwapIntervalEXT = cast(wglSwapIntervalEXTProc)wglGetProcAddress("wglSwapIntervalEXT");
235         if(wglSwapIntervalEXT is null)
236         {
237             MessageBox(NULL, "Could not load wglSwapIntervalEXT", "Error", MB_ICONERROR | MB_OK);
238             return false;
239         }
240         //Now, for the modern OpenGL
241         const int[19] attribList =
242         [
243             WGL_DRAW_TO_WINDOW_ARB, true,
244             WGL_SUPPORT_OPENGL_ARB, true,
245             WGL_DOUBLE_BUFFER_ARB, true,
246             WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
247             WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
248             WGL_COLOR_BITS_ARB, 32,
249             WGL_DEPTH_BITS_ARB, 24,
250             WGL_STENCIL_BITS_ARB, 8,
251             WGL_ALPHA_BITS_ARB, 8,
252             0, // End
253         ];
254 
255         int pixelFormat;
256         uint numFormats;
257 
258         if(!wglChoosePixelFormatARB(hdc, attribList.ptr, null, 1, &pixelFormat, &numFormats) || numFormats == 0)
259         {
260             MessageBox(NULL, "Could notchoose pixel format", "Error", MB_ICONERROR | MB_OK);
261             return false;
262         }
263 
264         auto oldHwnd = hwnd;
265         HDC oldHDC = hdc;
266         HGLRC oldGLContext = glContext;
267 
268         RECT rBorders;
269         GetWindowRect(hwnd, &rBorders);
270         RECT rNoBorders;
271         GetClientRect(hwnd, &rNoBorders);
272         RECT r;
273         r.left = rBorders.left*2 - rNoBorders.left;
274         r.right = rBorders.right*2 - rNoBorders.right;
275         r.top = rBorders.top*2 - rNoBorders.top;
276         r.bottom = rBorders.bottom*2 - rNoBorders.bottom;
277         //Create 
278         hwnd = createWindow(r.right - r.left, r.bottom - r.top);
279 
280         hdc = GetDC(hwnd);
281 
282         PIXELFORMATDESCRIPTOR newPFD;
283         DescribePixelFormat(hdc, pixelFormat, newPFD.sizeof, &newPFD);
284         SetPixelFormat(hdc, pixelFormat, &newPFD);
285 
286         int[7] contextAttribs = 
287         [
288             WGL_CONTEXT_MAJOR_VERSION_ARB, majorVersion,
289             WGL_CONTEXT_MINOR_VERSION_ARB, minorVersion,
290             WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
291             0
292         ];
293         glContext = wglCreateContextAttribsARB(hdc, null, contextAttribs.ptr);
294         if(glContext is null)
295         {
296             MessageBox(null, "Could not create Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
297             return false;
298         }
299         wglMakeCurrent(null, null);
300         wglDeleteContext(oldGLContext);
301         ReleaseDC(oldHwnd, oldHDC);
302         DestroyWindow(oldHwnd);
303         if(!wglMakeCurrent(hdc, glContext))
304         {
305             MessageBox(null, "Could not set Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
306             return false;
307         }
308         return true;
309 
310     }
311 
312     bool registerClass()
313     {
314         HINSTANCE hInstance = GetModuleHandle(null);
315         WNDCLASS wc;
316         //Register window class
317 
318         wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
319         wc.lpfnWndProc = &WndProc;
320         wc.cbClsExtra = 0;
321         wc.cbWndExtra = 0;
322         wc.hInstance = hInstance; //Application handle
323         wc.hIcon = LoadIcon(null, IDI_APPLICATION); //Big icon
324         wc.hCursor = LoadCursor(null, IDC_ARROW); //Cursor
325         wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW); //Background brush
326         wc.lpszMenuName = null; //Name of menu resource
327         wc.lpszClassName = winClassName; //Name to identify this class of windows
328 
329         if(!RegisterClass(cast(const(WNDCLASSW)*)&wc))
330         {
331             uint err = GetLastError();
332             wchar* buffer;
333             uint size = FormatMessage(
334                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
335                 cast(void*)null,err, 0, cast(LPWSTR)&buffer, 0, null);
336             wstring str = cast(wstring)buffer[0..size];
337             MessageBox(NULL, ("Window Registration Failed with message: "~str).ptr, "Error!", MB_ICONEXCLAMATION | MB_OK);
338             LocalFree(buffer);
339             return false;
340         }
341         return true;
342     }
343     package HWND createWindow(int width, int height) @nogc nothrow
344     {
345         return CreateWindowEx(
346             0,
347             winClassName,
348             winClassName, //Title
349             WS_OVERLAPPEDWINDOW | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE,
350             CW_USEDEFAULT, CW_USEDEFAULT,
351             width, height, HWND_DESKTOP, null, GetModuleHandle(null), null
352         );
353     }
354 
355     extern(Windows) LRESULT openWindow(out HWND hwnd, ref int width, ref int height)
356     {
357         static bool registeredClass = false;
358         if(!registeredClass)
359         {
360             if(!registerClass())
361                 return 0;
362         }
363         hwnd = createWindow(width, height);
364         if(hwnd == null)
365         {
366             MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
367             return 0;
368         }
369         hdc = GetDC(hwnd);
370         
371         return 1;
372     }
373 
374     void show(HWND hwnd)
375     {
376         ShowWindow(hwnd, SW_NORMAL);
377         UpdateWindow(hwnd);
378     }
379 
380     void poll()
381     {
382         MSG msg;
383         while(PeekMessage(&msg, cast(void*)null, 0,0, PM_REMOVE)) //GetMessage may be a lot better 
384         {
385             TranslateMessage(&msg);
386             DispatchMessage(&msg);
387         }
388     }
389 
390     void swapBuffer()
391     {
392         SwapBuffers(hdc);
393     }
394 
395     int[2] getWindowSize(HWND hwnd)
396     {
397         RECT rect;
398         GetClientRect(hwnd, &rect);
399         return [rect.right - rect.left, rect.bottom - rect.top];
400     }
401     void setWindowName(HWND hwnd, string name)
402     {
403         SetWindowTextA(hwnd, name.ptr);
404     }
405     int[2] getWindowBorder(HWND hwnd)
406     {
407         RECT rBorders;
408         GetWindowRect(hwnd, &rBorders);
409         RECT rNoBorders;
410         GetClientRect(hwnd, &rNoBorders);
411         RECT r;
412         r.left = rBorders.left - rNoBorders.left;
413         r.right = rBorders.right - rNoBorders.right;
414         r.top = rBorders.top - rNoBorders.top;
415         r.bottom = rBorders.bottom - rNoBorders.bottom;
416 
417         return[r.right - r.left, r.bottom - r.top];
418     }
419 
420     void setWindowSize(HWND hwnd, int width, int height)
421     {
422         int[2] borders = getWindowBorder(hwnd);
423         SetWindowPos(hwnd, null, 0, 0, width + borders[0], height+borders[1], SWP_NOMOVE);
424     }
425 
426     void setVsyncActive(bool active) @nogc nothrow @system
427     {
428         if(wglSwapIntervalEXT !is null)
429         {
430             wglSwapIntervalEXT(cast(int)active);
431         }
432     }
433 
434     extern(Windows) bool destroy_GL_Context()
435     {
436         if(!wglMakeCurrent(hdc, null))
437         {
438             MessageBox(NULL, "Could not detach OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK);
439             return false;
440         }
441         if(!wglDeleteContext(glContext))
442         {
443             MessageBox(NULL, "Could not delete OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK);
444             return false;
445         }
446         glContext = null;
447         return true;
448     }
449 }
450 
451 
452 
453 enum WGL_ARB_pixel_format= 1;
454 enum WGL_NUMBER_PIXEL_FORMATS_ARB     = 0x2000;
455 enum WGL_DRAW_TO_WINDOW_ARB           = 0x2001;
456 enum WGL_DRAW_TO_BITMAP_ARB           = 0x2002;
457 enum WGL_ACCELERATION_ARB             = 0x2003;
458 enum WGL_NEED_PALETTE_ARB             = 0x2004;
459 enum WGL_NEED_SYSTEM_PALETTE_ARB      = 0x2005;
460 enum WGL_SWAP_LAYER_BUFFERS_ARB       = 0x2006;
461 enum WGL_SWAP_METHOD_ARB              = 0x2007;
462 enum WGL_NUMBER_OVERLAYS_ARB          = 0x2008;
463 enum WGL_NUMBER_UNDERLAYS_ARB         = 0x2009;
464 enum WGL_TRANSPARENT_ARB              = 0x200A;
465 enum WGL_TRANSPARENT_RED_VALUE_ARB    = 0x2037;
466 enum WGL_TRANSPARENT_GREEN_VALUE_ARB  = 0x2038;
467 enum WGL_TRANSPARENT_BLUE_VALUE_ARB   = 0x2039;
468 enum WGL_TRANSPARENT_ALPHA_VALUE_ARB  = 0x203A;
469 enum WGL_TRANSPARENT_INDEX_VALUE_ARB  = 0x203B;
470 enum WGL_SHARE_DEPTH_ARB              = 0x200C;
471 enum WGL_SHARE_STENCIL_ARB            = 0x200D;
472 enum WGL_SHARE_ACCUM_ARB              = 0x200E;
473 enum WGL_SUPPORT_GDI_ARB              = 0x200F;
474 enum WGL_SUPPORT_OPENGL_ARB           = 0x2010;
475 enum WGL_DOUBLE_BUFFER_ARB            = 0x2011;
476 enum WGL_STEREO_ARB                   = 0x2012;
477 enum WGL_PIXEL_TYPE_ARB               = 0x2013;
478 enum WGL_COLOR_BITS_ARB               = 0x2014;
479 enum WGL_RED_BITS_ARB                 = 0x2015;
480 enum WGL_RED_SHIFT_ARB                = 0x2016;
481 enum WGL_GREEN_BITS_ARB               = 0x2017;
482 enum WGL_GREEN_SHIFT_ARB              = 0x2018;
483 enum WGL_BLUE_BITS_ARB                = 0x2019;
484 enum WGL_BLUE_SHIFT_ARB               = 0x201A;
485 enum WGL_ALPHA_BITS_ARB               = 0x201B;
486 enum WGL_ALPHA_SHIFT_ARB              = 0x201C;
487 enum WGL_ACCUM_BITS_ARB               = 0x201D;
488 enum WGL_ACCUM_RED_BITS_ARB           = 0x201E;
489 enum WGL_ACCUM_GREEN_BITS_ARB         = 0x201F;
490 enum WGL_ACCUM_BLUE_BITS_ARB          = 0x2020;
491 enum WGL_ACCUM_ALPHA_BITS_ARB         = 0x2021;
492 enum WGL_DEPTH_BITS_ARB               = 0x2022;
493 enum WGL_STENCIL_BITS_ARB             = 0x2023;
494 enum WGL_AUX_BUFFERS_ARB              = 0x2024;
495 enum WGL_NO_ACCELERATION_ARB          = 0x2025;
496 enum WGL_GENERIC_ACCELERATION_ARB     = 0x2026;
497 enum WGL_FULL_ACCELERATION_ARB        = 0x2027;
498 enum WGL_SWAP_EXCHANGE_ARB            = 0x2028;
499 enum WGL_SWAP_COPY_ARB                = 0x2029;
500 enum WGL_SWAP_UNDEFINED_ARB           = 0x202A;
501 enum WGL_TYPE_RGBA_ARB                = 0x202B;
502 enum WGL_TYPE_COLORINDEX_ARB          = 0x202C;
503 
504 
505 enum WGL_CONTEXT_MAJOR_VERSION_ARB           = 0x2091;
506 enum WGL_CONTEXT_MINOR_VERSION_ARB           = 0x2092;
507 enum WGL_CONTEXT_LAYER_PLANE_ARB             = 0x2093;
508 enum WGL_CONTEXT_FLAGS_ARB                   = 0x2094;
509 enum WGL_CONTEXT_PROFILE_MASK_ARB            = 0x9126;
510 
511 
512 enum WGL_CONTEXT_DEBUG_BIT_ARB               = 0x0001;
513 enum WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB  = 0x0002;
514 
515 enum WGL_CONTEXT_CORE_PROFILE_BIT_ARB         = 0x00000001;
516 enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB  = 0x00000002;
517 
518 
519 enum ERROR_INVALID_VERSION_ARB               = 0x2095;
520 enum ERROR_INVALID_PROFILE_ARB               = 0x2096;